/**
 *  @file BaseExceptions.h
 */
#include "../common/CppMemPool.h"
#include "BaseException.h"
#include "../text/CFString.h"

namespace cppflib
{

namespace exceptions
{


BaseException::BaseException(void)
{
   this->message = NULL;
   this->errCode = 0;
}

BaseException::BaseException(pcwstr_t message)
{
   this->message = new CFString(message);
   cppmempool::Retain(this->message);
   this->errCode = 0;
}

BaseException::BaseException(pcwstr_t message, int errCode)
{
   this->message = new CFString(message);
   cppmempool::Retain(this->message);
   this->errCode = errCode;
}

/**
 *  Redefine copy ctor here so that derived exception class can be copied.
 *  
 *  This is extremely important when the 'catch' clause declared to be passed by value
 *  instead of passed by reference. Since for the passed-by-value case, an extra exception
 *  instance is copied and the message string here has to be 'Retain' instead of merely
 *  copying the pointer.
 */
BaseException::BaseException(const BaseException &exp)
{
   this->message = exp.message;
   cppmempool::Retain(this->message);
   this->errCode = exp.errCode;
}

BaseException::~BaseException(void)
{
   cppmempool::Release(this->message);
}

void BaseException::SetMsg(pcwstr_t message)
{
   if (this->message) cppmempool::Release(this->message);
   this->message = new CFString(message);
   cppmempool::Retain(this->message);
}

void BaseException::SetErrCode(int errCode)
{
   this->errCode = errCode;
}

pcwstr_t BaseException::GetMsg()
{
   if (CFString::IsNullOrEmpty(this->message))
      return _S("");

   return static_cast<pcwstr_t>(*this->message);
}

int BaseException::GetErrCode()
{
   return this->errCode;
}

/**
 *  Don't new an exception instance and throw. Instead, throwing a local instance is alright. C++ 
 *  will throw a copy of the local instance.
 *
 *  Try to throw like this:
 *
 *     throw BaseException(_S("exception occurs"));
 *
 *  and catch like this:
 *
 *     try {
 *        ...
 *        ...
 *     }
 *     catch (BaseException &exp) {
 *        ....
 *     }
 *
 *
 *  If somebody really want to new an exception instance, use global 
 *
 *     ::operator new(cppflib::cppmempool::NEWCLASS) BaseException();
 *  
 */
void *BaseException::operator new(sz_t size)
{
   throw "Don't throw a heap allocated exception but instead throw a local instance of exception to prevent memory leak.";
}

/**
 *  This should not be called directly. Use cppmempool::Release instead.
 */ 
void BaseException::operator delete(void *p)
{
   // use this to check whether this 'operator delete' function is called during an exception
   if (std::uncaught_exception()) {
      cppmempool::Release(p);
   }
   else {
      throw "Don't call delete directly! Use 'cppflib::cppmempool::Release' instead.";
   }
}

/**
 *  Don't call this one, see operator new above
 */
void *BaseException::operator new[](sz_t size)
{
   throw "Don't throw a heap allocated exception but instead throw a local instance of exception to prevent memory leak.";
}

/**
 *  Again, this should not be called
 */
void BaseException::operator delete[](void *p)
{
   cppmempool::Release(p);
}

}  // end of namespace exceptions

}  // end of namespace cppflib

